home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / cmds / Mail / send.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-02-08  |  11.0 KB  |  512 lines

  1. /*
  2.  * Copyright (c) 1980 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms are permitted
  6.  * provided that the above copyright notice and this paragraph are
  7.  * duplicated in all such forms and that any documentation,
  8.  * advertising materials, and other materials related to such
  9.  * distribution and use acknowledge that the software was developed
  10.  * by the University of California, Berkeley.  The name of the
  11.  * University may not be used to endorse or promote products derived
  12.  * from this software without specific prior written permission.
  13.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  14.  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  15.  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  16.  */
  17.  
  18. #ifndef lint
  19. static char sccsid[] = "@(#)send.c    5.17 (Berkeley) 8/9/88";
  20. #endif /* not lint */
  21.  
  22. #include "rcv.h"
  23.  
  24. /*
  25.  * Mail -- a mail program
  26.  *
  27.  * Mail to others.
  28.  */
  29.  
  30. /*
  31.  * Send message described by the passed pointer to the
  32.  * passed output buffer.  Return -1 on error.
  33.  * Adjust the status: field if need be.
  34.  * If doign is given, suppress ignored header fields.
  35.  * prefix is a string to prepend to each output line.
  36.  */
  37. send(mp, obuf, doign, prefix)
  38.     register struct message *mp;
  39.     FILE *obuf;
  40.     struct ignoretab *doign;
  41.     char *prefix;
  42. {
  43.     long count;
  44.     register FILE *ibuf;
  45.     char line[LINESIZE];
  46.     int ishead, infld, ignoring, dostat, firstline;
  47.     register char *cp, *cp2;
  48.     register int c;
  49.     int length;
  50.  
  51.     ibuf = setinput(mp);
  52.     count = mp->m_size;
  53.     ishead = 1;
  54.     dostat = doign == 0 || !isign("status", doign);
  55.     infld = 0;
  56.     firstline = 1;
  57.     /*
  58.      * Process headers first
  59.      */
  60.     while (count > 0 && ishead) {
  61.         if (fgets(line, LINESIZE, ibuf) == NULL)
  62.             break;
  63.         count -= length = strlen(line);
  64.         if (firstline) {
  65.             /* 
  66.              * First line is the From line, so no headers
  67.              * there to worry about
  68.              */
  69.             firstline = 0;
  70.             ignoring = doign == ignoreall;
  71.         } else if (line[0] == '\n') {
  72.             /*
  73.              * If line is blank, we've reached end of
  74.              * headers, so force out status: field
  75.              * and note that we are no longer in header
  76.              * fields
  77.              */
  78.             if (dostat) {
  79.                 statusput(mp, obuf, prefix);
  80.                 dostat = 0;
  81.             }
  82.             ishead = 0;
  83.             ignoring = doign == ignoreall;
  84.         } else if (infld && (line[0] == ' ' || line[0] == '\t')) {
  85.             /*
  86.              * If this line is a continuation (via space or tab)
  87.              * of a previous header field, just echo it
  88.              * (unless the field should be ignored).
  89.              * In other words, nothing to do.
  90.              */
  91.         } else {
  92.             /*
  93.              * Pick up the header field if we have one.
  94.              */
  95.             for (cp = line; (c = *cp++) && c != ':' && !isspace(c);)
  96.                 ;
  97.             cp2 = --cp;
  98.             while (isspace(*cp++))
  99.                 ;
  100.             if (cp[-1] != ':') {
  101.                 /*
  102.                  * Not a header line, force out status:
  103.                  * This happens in uucp style mail where
  104.                  * there are no headers at all.
  105.                  */
  106.                 if (dostat) {
  107.                     statusput(mp, obuf, prefix);
  108.                     dostat = 0;
  109.                 }
  110.                 if (doign != ignoreall)
  111.                     /* add blank line */
  112.                     (void) putc('\n', obuf);
  113.                 ishead = 0;
  114.                 ignoring = 0;
  115.             } else {
  116.                 /*
  117.                  * If it is an ignored field and
  118.                  * we care about such things, skip it.
  119.                  */
  120.                 *cp2 = 0;    /* temporarily null terminate */
  121.                 if (doign && isign(line, doign))
  122.                     ignoring = 1;
  123.                 else if ((line[0] == 's' || line[0] == 'S') &&
  124.                      strcasecmp(line, "status") == 0) {
  125.                     /*
  126.                      * If the field is "status," go compute
  127.                      * and print the real Status: field
  128.                      */
  129.                     if (dostat) {
  130.                         statusput(mp, obuf, prefix);
  131.                         dostat = 0;
  132.                     }
  133.                     ignoring = 1;
  134.                 } else {
  135.                     ignoring = 0;
  136.                     *cp2 = c;    /* restore */
  137.                 }
  138.                 infld = 1;
  139.             }
  140.         }
  141.         if (!ignoring) {
  142.             if (prefix != NOSTR && length > 1)
  143.                 fputs(prefix, obuf);
  144.             (void) fwrite(line, sizeof *line, length, obuf);
  145.             if (ferror(obuf))
  146.                 return -1;
  147.         }
  148.     }
  149.     /*
  150.      * Copy out message body
  151.      */
  152.     if (doign == ignoreall)
  153.         count--;        /* skip final blank line */
  154.     if (prefix != NOSTR)
  155.         while (count > 0) {
  156.             if (fgets(line, LINESIZE, ibuf) == NULL) {
  157.                 c = 0;
  158.                 break;
  159.             }
  160.             count -= c = strlen(line);
  161.             if (c > 1)
  162.                 fputs(prefix, obuf);
  163.             (void) fwrite(line, sizeof *line, c, obuf);
  164.             if (ferror(obuf))
  165.                 return -1;
  166.         }
  167.     else
  168.         while (count > 0) {
  169.             c = count < LINESIZE ? count : LINESIZE;
  170.             if ((c = fread(line, sizeof *line, c, ibuf)) <= 0)
  171.                 break;
  172.             count -= c;
  173.             if (fwrite(line, sizeof *line, c, obuf) != c)
  174.                 return -1;
  175.         }
  176.     if (doign == ignoreall && c > 0 && line[c - 1] != '\n')
  177.         /* no final blank line */
  178.         if ((c = getc(ibuf)) != EOF && putc(c, obuf) == EOF)
  179.             return -1;
  180.     return 0;
  181. }
  182.  
  183. /*
  184.  * Output a reasonable looking status field.
  185.  */
  186. statusput(mp, obuf, prefix)
  187.     register struct message *mp;
  188.     FILE *obuf;
  189.     char *prefix;
  190. {
  191.     char statout[3];
  192.     register char *cp = statout;
  193.  
  194.     if (mp->m_flag & MREAD)
  195.         *cp++ = 'R';
  196.     if ((mp->m_flag & MNEW) == 0)
  197.         *cp++ = 'O';
  198.     *cp = 0;
  199.     if (statout[0])
  200.         fprintf(obuf, "%sStatus: %s\n",
  201.             prefix == NOSTR ? "" : prefix, statout);
  202. }
  203.  
  204. /*
  205.  * Interface between the argument list and the mail1 routine
  206.  * which does all the dirty work.
  207.  */
  208. mail(to, cc, bcc, smopts, subject)
  209.     struct name *to, *cc, *bcc, *smopts;
  210.     char *subject;
  211. {
  212.     struct header head;
  213.  
  214.     head.h_to = to;
  215.     head.h_subject = subject;
  216.     head.h_cc = cc;
  217.     head.h_bcc = bcc;
  218.     head.h_smopts = smopts;
  219.     mail1(&head, 0);
  220.     return(0);
  221. }
  222.  
  223.  
  224. /*
  225.  * Send mail to a bunch of user names.  The interface is through
  226.  * the mail routine below.
  227.  */
  228. sendmail(str)
  229.     char *str;
  230. {
  231.     struct header head;
  232.  
  233.     head.h_to = extract(str, GTO);
  234.     head.h_subject = NOSTR;
  235.     head.h_cc = NIL;
  236.     head.h_bcc = NIL;
  237.     head.h_smopts = NIL;
  238.     mail1(&head, 0);
  239.     return(0);
  240. }
  241.  
  242. /*
  243.  * Mail a message on standard input to the people indicated
  244.  * in the passed header.  (Internal interface).
  245.  */
  246. mail1(hp, printheaders)
  247.     struct header *hp;
  248. {
  249.     char *cp;
  250.     int pid;
  251.     char **namelist;
  252.     struct name *to;
  253.     FILE *mtf;
  254.  
  255.     /*
  256.      * Collect user's mail from standard input.
  257.      * Get the result as mtf.
  258.      */
  259.     if ((mtf = collect(hp, printheaders)) == NULL)
  260.         return;
  261.     if (value("interactive") != NOSTR)
  262.         if (value("askcc") != NOSTR)
  263.             grabh(hp, GCC);
  264.         else {
  265.             printf("EOT\n");
  266.             (void) fflush(stdout);
  267.         }
  268.     if (fsize(mtf) == 0)
  269.         if (hp->h_subject == NOSTR)
  270.             printf("No message, no subject; hope that's ok\n");
  271.         else
  272.             printf("Null message body; hope that's ok\n");
  273.     /*
  274.      * Now, take the user names from the combined
  275.      * to and cc lists and do all the alias
  276.      * processing.
  277.      */
  278.     senderr = 0;
  279.     to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc)));
  280.     if (to == NIL) {
  281.         printf("No recipients specified\n");
  282.         senderr++;
  283.     }
  284.     /*
  285.      * Look through the recipient list for names with /'s
  286.      * in them which we write to as files directly.
  287.      */
  288.     to = outof(to, mtf, hp);
  289.     if (senderr)
  290.         savedeadletter(mtf);
  291.     to = elide(to);
  292.     if (count(to) == 0)
  293.         goto out;
  294.     fixhead(hp, to);
  295.     if ((mtf = infix(hp, mtf)) == NULL) {
  296.         fprintf(stderr, ". . . message lost, sorry.\n");
  297.         return;
  298.     }
  299.     namelist = unpack(cat(hp->h_smopts, to));
  300.     if (debug) {
  301.         char **t;
  302.  
  303.         printf("Sendmail arguments:");
  304.         for (t = namelist; *t != NOSTR; t++)
  305.             printf(" \"%s\"", *t);
  306.         printf("\n");
  307.         goto out;
  308.     }
  309.     if ((cp = value("record")) != NOSTR)
  310.         (void) savemail(expand(cp), mtf);
  311.     /*
  312.      * Fork, set up the temporary mail file as standard
  313.      * input for "mail", and exec with the user list we generated
  314.      * far above.
  315.      */
  316.     pid = fork();
  317.     if (pid == -1) {
  318.         perror("fork");
  319.         savedeadletter(mtf);
  320.         goto out;
  321.     }
  322.     if (pid == 0) {
  323.         if (access(POSTAGE, 0) == 0) {
  324.             FILE *postage;
  325.  
  326.             if ((postage = fopen(POSTAGE, "a")) != NULL) {
  327.                 fprintf(postage, "%s %d %ld\n", myname,
  328.                     count(to), fsize(mtf));
  329.                 (void) fclose(postage);
  330.             }
  331.         }
  332.         prepare_child(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT)|
  333.             sigmask(SIGTSTP)|sigmask(SIGTTIN)|sigmask(SIGTTOU),
  334.             fileno(mtf), -1);
  335.         if ((cp = value("sendmail")) != NOSTR)
  336.             cp = expand(cp);
  337.         else
  338.             cp = SENDMAIL;
  339.         execv(cp, namelist);
  340.         perror(cp);
  341.         _exit(1);
  342.     }
  343.     if (value("verbose") != NOSTR)
  344.         (void) wait_child(pid);
  345.     else
  346.         free_child(pid);
  347. out:
  348.     (void) fclose(mtf);
  349. }
  350.  
  351. /*
  352.  * Fix the header by glopping all of the expanded names from
  353.  * the distribution list into the appropriate fields.
  354.  */
  355. fixhead(hp, tolist)
  356.     struct header *hp;
  357.     struct name *tolist;
  358. {
  359.     register struct name *np;
  360.  
  361.     hp->h_to = NIL;
  362.     hp->h_cc = NIL;
  363.     hp->h_bcc = NIL;
  364.     for (np = tolist; np != NIL; np = np->n_flink)
  365.         if ((np->n_type & GMASK) == GTO)
  366.             hp->h_to =
  367.                 cat(hp->h_to, nalloc(np->n_name, np->n_type));
  368.         else if ((np->n_type & GMASK) == GCC)
  369.             hp->h_cc =
  370.                 cat(hp->h_cc, nalloc(np->n_name, np->n_type));
  371.         else if ((np->n_type & GMASK) == GBCC)
  372.             hp->h_bcc =
  373.                 cat(hp->h_bcc, nalloc(np->n_name, np->n_type));
  374. }
  375.  
  376. /*
  377.  * Prepend a header in front of the collected stuff
  378.  * and return the new file.
  379.  */
  380. FILE *
  381. infix(hp, fi)
  382.     struct header *hp;
  383.     FILE *fi;
  384. {
  385.     extern char tempMail[];
  386.     register FILE *nfo, *nfi;
  387.     register int c;
  388.  
  389.     if ((nfo = fopen(tempMail, "w")) == NULL) {
  390.         perror(tempMail);
  391.         return(fi);
  392.     }
  393.     if ((nfi = fopen(tempMail, "r")) == NULL) {
  394.         perror(tempMail);
  395.         (void) fclose(nfo);
  396.         return(fi);
  397.     }
  398.     (void) remove(tempMail);
  399.     (void) puthead(hp, nfo, GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA);
  400.     c = getc(fi);
  401.     while (c != EOF) {
  402.         (void) putc(c, nfo);
  403.         c = getc(fi);
  404.     }
  405.     if (ferror(fi)) {
  406.         perror("read");
  407.         rewind(fi);
  408.         return(fi);
  409.     }
  410.     (void) fflush(nfo);
  411.     if (ferror(nfo)) {
  412.         perror(tempMail);
  413.         (void) fclose(nfo);
  414.         (void) fclose(nfi);
  415.         rewind(fi);
  416.         return(fi);
  417.     }
  418.     (void) fclose(nfo);
  419.     (void) fclose(fi);
  420.     rewind(nfi);
  421.     return(nfi);
  422. }
  423.  
  424. /*
  425.  * Dump the to, subject, cc header on the
  426.  * passed file buffer.
  427.  */
  428. puthead(hp, fo, w)
  429.     struct header *hp;
  430.     FILE *fo;
  431. {
  432.     register int gotcha;
  433.  
  434.     gotcha = 0;
  435.     if (hp->h_to != NIL && w & GTO)
  436.         fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++;
  437.     if (hp->h_subject != NOSTR && w & GSUBJECT)
  438.         fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++;
  439.     if (hp->h_cc != NIL && w & GCC)
  440.         fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++;
  441.     if (hp->h_bcc != NIL && w & GBCC)
  442.         fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++;
  443.     if (gotcha && w & GNL)
  444.         (void) putc('\n', fo);
  445.     return(0);
  446. }
  447.  
  448. /*
  449.  * Format the given header line to not exceed 72 characters.
  450.  */
  451. fmt(str, np, fo, comma)
  452.     char *str;
  453.     register struct name *np;
  454.     FILE *fo;
  455.     int comma;
  456. {
  457.     register col, len;
  458.  
  459.     comma = comma ? 1 : 0;
  460.     col = strlen(str);
  461.     if (col)
  462.         fputs(str, fo);
  463.     for (; np != NIL; np = np->n_flink) {
  464.         if (np->n_flink == NIL)
  465.             comma = 0;
  466.         len = strlen(np->n_name);
  467.         col++;        /* for the space */
  468.         if (col + len + comma > 72 && col > 4) {
  469.             fputs("\n    ", fo);
  470.             col = 4;
  471.         } else
  472.             putc(' ', fo);
  473.         fputs(np->n_name, fo);
  474.         if (comma)
  475.             putc(',', fo);
  476.         col += len + comma;
  477.     }
  478.     putc('\n', fo);
  479. }
  480.  
  481. /*
  482.  * Save the outgoing mail on the passed file.
  483.  */
  484.  
  485. /*ARGSUSED*/
  486. savemail(name, fi)
  487.     char name[];
  488.     register FILE *fi;
  489. {
  490.     register FILE *fo;
  491.     char buf[BUFSIZ];
  492.     register i;
  493.     time_t now, time();
  494.     char *ctime();
  495.  
  496.     if ((fo = fopen(name, "a")) == NULL) {
  497.         perror(name);
  498.         return (-1);
  499.     }
  500.     (void) time(&now);
  501.     fprintf(fo, "From %s %s", myname, ctime(&now));
  502.     while ((i = fread(buf, 1, sizeof buf, fi)) > 0)
  503.         (void) fwrite(buf, 1, i, fo);
  504.     (void) putc('\n', fo);
  505.     (void) fflush(fo);
  506.     if (ferror(fo))
  507.         perror(name);
  508.     (void) fclose(fo);
  509.     rewind(fi);
  510.     return (0);
  511. }
  512.